package flca.voicenav.util;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import android.util.Log;

public class FileUtils 
{
	private static String TAG = "flca-fileutils";

	private FileUtils() {
	}

	/**
	 * Read the contents of a file given URL
	 * 
	 * @param aFilename
	 * @return
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static String readFile(URL aUrl) throws FileNotFoundException,
			IOException {
		return readFile(aUrl.getFile());
	}

	/**
	 * Read the contents of a file given a filename
	 * 
	 * @param aFilename
	 * @return
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static String readFile(String aFilename)
			throws FileNotFoundException, IOException {
		StringBuffer sb = new StringBuffer();

		FileInputStream fin = null;
		BufferedInputStream bin = null;
		try {
			fin = new FileInputStream(aFilename);
			bin = new BufferedInputStream(fin);
			int ch = 0;
			while ((ch = bin.read()) > -1) {
				sb.append((char) ch);
			}

			return sb.toString();
		} catch (FileNotFoundException e) {
			Log.e(TAG, "Could not find file: " + aFilename);
			throw e;
		} catch (IOException e) {
			Log.e(TAG, "Error reading file: " + aFilename, e);
			throw e;
		} finally {
			if (bin != null) {
				bin.close();
			}
			if (fin != null) {
				fin.close();
			}
		}
	}

	/**
	 * Read the contents of a given resource, given a resourcename
	 * 
	 * @param aInstance
	 * @param aResourceName
	 * @return
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static String readResource(Object aInstance, String aResourceName)
			throws FileNotFoundException, IOException {
		return readResource(aInstance.getClass(), aResourceName);
	}

	/**
	 * Read the contents of a given resource, given a resourcename
	 * 
	 * @param aInstance
	 * @param aResourceName
	 * @return
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static String readResource(Class<?> aClass, String aResourceName)
			throws FileNotFoundException, IOException {
		InputStream is = null;
		BufferedReader reader = null;
		try {
			StringBuffer sb = new StringBuffer();

			is = aClass.getResourceAsStream(aResourceName);
			reader = new BufferedReader(new InputStreamReader(is));
			char[] buf = new char[1024];
			int numRead = 0;
			while ((numRead = reader.read(buf)) != -1) {
				sb.append(String.valueOf(buf, 0, numRead));
				buf = new char[1024];
			}
			return sb.toString();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			throw e;
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		} finally {
			if (reader != null) {
				reader.close();
			}
			if (is != null) {
				is.close();
			}
		}
	}

	/**
	 * Save the given content to a given filename
	 * 
	 * @param aFilename
	 * @param aContent
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static void saveFile(String aFilename, String aContent)
			throws FileNotFoundException, IOException {
		String dirs = null;
		if (aFilename.lastIndexOf("/") > 0) {
			dirs = aFilename.substring(0, aFilename.lastIndexOf("/"));
		} else if (aFilename.lastIndexOf("\\") > 0) {
			dirs = aFilename.substring(0, aFilename.lastIndexOf("\\"));
		}

		if (dirs != null) {
			new File(dirs).mkdirs();
			File f = new File(aFilename);
			FileOutputStream fos = new FileOutputStream(f);
			fos.write(aContent.getBytes());
			fos.close();
		} else {
			Log.e(TAG, "saveFile failed for " + aFilename);
		}
	}

	public static void saveFile(File aFile, String aContent)
			throws FileNotFoundException, IOException {
		saveFile(aFile.getPath(), aContent);
	}

	public static List<String> findDirs(String aSeachRootDir, boolean aRecursive) {
		List<String> result = new ArrayList<String>();

		doFindDirs(aSeachRootDir, new DirFilter(), aRecursive, result);

		return result;
	}

	public static List<String> findFiles(String aSeachRootDir,
			boolean aRecursive) {
		return findFiles(aSeachRootDir, null, aRecursive);
	}

	public static List<String> findFiles(String aSeachRootDir,
			FileFilter aFilter, boolean aRecursive) {
		List<String> result = new ArrayList<String>();

		doFindFiles(aSeachRootDir, aFilter, aRecursive, result);

		return result;
	}

	private static void doFindFiles(String aSeachRootDir, FileFilter aFilter,
			boolean aRecursive, List<String> aFiles) {
		File f = new File(aSeachRootDir);
		File files[] = null;

		if (f != null) {
			if (aFilter == null) {
				files = f.listFiles();
			} else {
				files = f.listFiles(aFilter);
			}
		}

		if (files != null) {
			for (File file : files) {
				if (!file.isDirectory() && !file.isHidden()) {
					String fname = file.getPath();
					aFiles.add(fname);
				} else if (file.isDirectory() && aRecursive) {
					String pathname = file.getPath();
					doFindFiles(pathname, aFilter, aRecursive, aFiles);
				}
			}
		}
	}

	private static void doFindDirs(String aSeachRootDir, FileFilter aFilter,
			boolean aRecursive, List<String> aFiles) {
		File f = new File(aSeachRootDir);
		File files[] = null;
		files = f.listFiles(aFilter);

		if (files != null) {
			for (File file : files) {
				String fname = file.getPath();
				aFiles.add(fname);
			}
		}
	}

//	public static File lookupFile(String basePath, String path) {
//		File f = null;
//
//		if (path.substring(0, 1).equals("/")
//				|| path.substring(0, 1).equals(File.separator)
//				|| (System.getProperty("os.name").matches(".*Windows.*") && path
//						.substring(0, 1).matches("^([A-Z]:)?[/\\\\].*"))) {
//			// Try absolute path
//			f = new File(path);
//			if (Log.isDebugEnabled())
//				Log.debug("Trying absolute file " + f);
//			if (!f.canRead() || !f.isFile()) {
//				if (Log.isWarnEnabled())
//					Log.warn("File " + f
//							+ " does not exist or is not readable.");
//				f = null;
//			}
//		} else if (basePath != null && basePath.length() > 0) {
//			// Look in base directory
//			// String contextPath = getServletContext().getRealPath("/");
//			f = new File(basePath, path);
//			if (Log.isDebugEnabled())
//				Log.debug("Trying file " + f);
//			if (!f.canRead() || !f.isFile()) {
//				if (Log.isWarnEnabled())
//					Log.warn("File " + f
//							+ " does not exist or is not readable.");
//				f = null;
//			}
//		}
//		if (f == null)
//			if (Log.isWarnEnabled())
//				Log.warn("File for path " + path + " could not be located.");
//			else if (Log.isDebugEnabled())
//				Log.debug("File for path " + path + " could not be located.");
//		return f;
//	}
//
	static class DirFilter implements FileFilter {

		public boolean accept(File file) {
			return file.isDirectory() && !file.isHidden();
		}
	}


	/**
	 * This resturn the URL given a resourcename (eq "/resources/my-file.txt")
	 * or full filename "/home/tmp/my-file.txt" or something
	 * http://www.robin.com/resources/my-file.txt If the resource or file does
	 * not exist, null will be returned
	 * 
	 * @param aFileOrResourceName
	 * @return
	 */
	public static URL getExistingUrl(String aFileOrResourceName) {
		return getExistingUrl(FileUtils.class, aFileOrResourceName);
	}

	/**
	 * This resturn the URL given a resourcename (eq "/resources/my-file.txt")
	 * or full filename "/home/tmp/my-file.txt" or something
	 * http://www.robin.com/resources/my-file.txt If the resource or file does
	 * not exist, null will be returned
	 * 
	 * @param aFileOrResourceName
	 * @return
	 */
	public static URL getExistingUrl(Class<?> aClass, String aFileOrResourceName) {
		URL url = aClass.getResource(aFileOrResourceName);
		if (url != null) {
			return url;
		} else {
			File f = new File(aFileOrResourceName);
			if (f.exists()) {
				try {
					return new URL("file://" + f.getPath());
				} catch (MalformedURLException e) {
					Log.e(TAG, "error creating URL from filespec " + e);
					return null;
				}
			} else {
				return null;
			}
		}
	}

	public static String FILE_SEP = null;

	public static String appendSeperator(String aValue) {
		if (FILE_SEP == null) {
			FILE_SEP = System.getProperty("file.separator");
		}

		if (aValue != null && aValue.endsWith("/") || aValue.endsWith("\\")) {
			return aValue;
		} else {
			return aValue + FILE_SEP;
		}
	}

	public static String getFilenameOnly(File aFile) {
		if (aFile != null) {
			String s = aFile.getName();
			return s;
		} else {
			return null;
		}
	}

	public static File getFileDirOnly(File aFile) {
		if (aFile != null) {
			String s1 = aFile.getPath();
			String s2 = aFile.getName();
			return new File(s1.substring(0, s1.length() - s2.length()));
		} else {
			return null;
		}
	}


	public static boolean isBinary(File aFile)
	{
		try {
			String s = readFile(aFile.getAbsolutePath());
			char chars[] = s.toCharArray();
			for (char c : chars) {
				if ((int) c == 0) {
					return true;
				}
			}
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			return true;
		} 
	}
	
	/**
	 * return the temp folder
	 * @return
	 */
	public static String getTempDir() {
		return "/tmp";
	}
	
	/**
	 * Given a file, return the file one level up.
	 * Example /home/robin/test returns /home/robin.
	 * This method does NOT check if the inout file exists!
	 * @param aFile
	 * @return
	 */
	public static File getParentDir(File aFile)
	{
		String filename = aFile.getAbsolutePath();
		String sep = "/";
		if (filename.indexOf(sep) >= 0) {
			if (filename.endsWith(sep)) {
				filename = filename.substring(0, filename.length() - 1);
			}
			return new File(filename.substring(0, filename.lastIndexOf(sep)));
		} else {
			return aFile;
		}
	}
}
